﻿namespace CAION2.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Windows;
    using System.Xml.Linq;

    using Classes;
    using Commands;
    using Helpers;
    using Models;
    using Properties;

    using FirstFloor.ModernUI.Windows.Controls;

    class StigmasViewModel : ViewModelBase
    {
        #region Vars

        private readonly Dictionary<int, Tuple<int, int>> _sums;
        private int _abyssPointSum;
        private string _characterClass;
        private byte _characterLevel;
        private int _crystalShardSum;
        private bool _currentCharacterRace;
        private List<StigmaPlaceViewModel> _enhantAttPlaces;
        private List<StigmaPlaceViewModel> _enhantDefPlaces;
        private List<string> _gameClasses;
        private Byte _maxCharacterLevel;
        private List<StigmaPlaceViewModel> _places;
        private Visibility _placesVisible;
        #endregion

        #region Properties

        public int AbyssPointSum
        {
            get { return _abyssPointSum; }
            private set
            {
                if (Equals(_abyssPointSum, value)) return;
                _abyssPointSum = value;

                OnPropertyChanged("AbyssPointSum");
            }
        }

        public int CrystalShardSum
        {
            get { return _crystalShardSum; }
            private set
            {
                if (Equals(_crystalShardSum, value)) return;
                _crystalShardSum = value;

                OnPropertyChanged("CrystalShardSum");
            }
        }

        public string CurrentCharacterClass
        {
            get { return _characterClass; }
            set
            {
                if (Equals(_characterClass, value)) return;
                _characterClass = value;

                OnPropertyChanged("CurrentCharacterClass");

                Mediator.Instance.NotifyColleagues(Messages.StigmasGameClassSend, CurrentCharacterClass);
                PlacesVisible = String.IsNullOrEmpty(CurrentCharacterClass) ? Visibility.Hidden : Visibility.Visible;

                ClearPlaces();
                StatusIsChange();
                if (!SaveCharacterCommand.IsEnabled) SaveCharacterCommand.IsEnabled = true;
            }
        }

        public byte CurrentCharacterLevel
        {
            get { return _characterLevel; }
            set
            {
                if (Equals(_characterLevel, value)) return;
                _characterLevel = value;

                OnPropertyChanged("CurrentCharacterLevel");
                Mediator.Instance.NotifyColleagues(Messages.StigmasCharacterLevelSend, CurrentCharacterLevel);

                StatusIsChange();
            }
        }

        public bool CurrentCharacterRace
        {
            get { return _currentCharacterRace; }
            set
            {
                _currentCharacterRace = value;
                OnPropertyChanged("CurrentCharacterRace");

                ClassConfiguration.SetValue("StigmaRaceA", _currentCharacterRace);
                StatusIsChange();
            }
        }

        public List<StigmaPlaceViewModel> EnhantAttPlaces
        {
            get { return _enhantAttPlaces; }
            private set
            {
                if (Equals(_enhantAttPlaces, value)) return;
                _enhantAttPlaces = value;
            }
        }

        public List<StigmaPlaceViewModel> EnhantDefPlaces
        {
            get { return _enhantDefPlaces; }
            private set
            {
                if (Equals(_enhantDefPlaces, value)) return;
                _enhantDefPlaces = value;
            }
        }

        public List<string> GameClasses
        {
            get { return _gameClasses; }
            private set
            {
                if (Equals(_gameClasses, value)) return;
                _gameClasses = value;

                OnPropertyChanged("GameClasses");
            }
        }

        public byte MaxCharacterLevel
        {
            get { return _maxCharacterLevel; }
            private set
            {
                if (Equals(_maxCharacterLevel, value)) return;
                _maxCharacterLevel = value;

                OnPropertyChanged("MaxCharacterLevel");
            }
        }
        public List<StigmaPlaceViewModel> Places
        {
            get { return _places; }
            private set
            {
                if (Equals(_places, value)) return;
                _places = value;
            }
        }
        public Visibility PlacesVisible
        {
            get { return _placesVisible; }
            set
            {
                if (Equals(_placesVisible, value)) return;
                _placesVisible = value;

                OnPropertyChanged("PlacesVisible");
            }
        }
        #endregion

        #region Commands
        
        public RelayCommand ClearPlacesCommand { get; private set; }

        public RelayCommand LoadCharacterCommand { get; private set; }

        public RelayCommand SaveCharacterCommand { get; private set; }
        
        #endregion


        public StigmasViewModel()
        {
            WireCommands();
            
            //TODO: Переделать на Tuple<>
            Mediator.Instance.Register(o => UpdateGeneralInfo((int[])o), Messages.StigmaSumsSend);
            Mediator.Instance.Register(o => GetNewStigmaFromEnhanted((Tuple<bool, StigmaCardViewModel>)o), Messages.EnhantedStigmaSend);

            MaxCharacterLevel = ClassConfiguration.CharacterMaximumLevel;
            CurrentCharacterLevel = MaxCharacterLevel;
            CurrentCharacterRace = ClassConfiguration.GetBool("StigmaRaceA");

            _sums = new Dictionary<int, Tuple<int, int>>();
            
            GameClasses = new List<string>
                {
                    Resources.Class_Fighter,
                    Resources.Class_Knight,
                    Resources.Class_Assassin,
                    Resources.Class_Ranger,
                    Resources.Class_Wizard,
                    Resources.Class_Elementalist,
                    Resources.Class_Priest,
                    Resources.Class_Chanter,
                    Resources.Class_Bard,
                    Resources.Class_Gunner,
                    Resources.Class_Rider
                };

            PlacesVisible = Visibility.Hidden;

            Places = new List<StigmaPlaceViewModel>
                     {
                         new StigmaPlaceViewModel(false, 20, 1),
                         new StigmaPlaceViewModel(false, 20, 2),
                         new StigmaPlaceViewModel(false, 30, 3),
                         new StigmaPlaceViewModel(false, 40, 4),
                         new StigmaPlaceViewModel(false, 50, 5),
                         new StigmaPlaceViewModel(false, 55, 6),
                         new StigmaPlaceViewModel(true, 45, 7),
                         new StigmaPlaceViewModel(true, 45, 8),
                         new StigmaPlaceViewModel(true, 50, 9),
                         new StigmaPlaceViewModel(true, 52, 10),
                         new StigmaPlaceViewModel(true, 55, 11),
                         new StigmaPlaceViewModel(true, 58, 12)
                     };

            EnhantDefPlaces = new List<StigmaPlaceViewModel>
            {
                new StigmaPlaceViewModel(true, 1, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 2, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 3, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 4, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 5, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(false, 6, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(false, 7, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(false, 8, new StigmaCardViewModel())
            };

            EnhantAttPlaces = new List<StigmaPlaceViewModel>
            {
                new StigmaPlaceViewModel(true, 1, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 2, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 3, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 4, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(true, 5, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(false, 6, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(false, 7, new StigmaCardViewModel()),
                new StigmaPlaceViewModel(false, 8, new StigmaCardViewModel())
            };
        }
        
        private void CalculateSums(IList<int> info)
        {
            if (_sums.ContainsKey(info[0]))
            {
                _sums[info[0]] = new Tuple<int, int>(info[1], info[2]);
            }
            else
            {
                _sums.Add(info[0], new Tuple<int, int>(info[1], info[2]));
            }

            CrystalShardSum = _sums.Values.Sum(s => s.Item1);
            AbyssPointSum = _sums.Values.Sum(s => s.Item2);
        }

        private void ClearPlaces()
        {
            foreach (StigmaPlaceViewModel place in Places)
            {
                place.DeletePlace();
            }
        }
        
        private void FillEnchantedAttStigmas()
        {
            //Топовая стигма
            Dictionary<string, DataRow> stigmas =
                StigmaSet.CreateStigmaListForStigma(StigmaSet.GetGroupOfTopStigma(CurrentCharacterClass, false), CurrentCharacterRace);
            EnhantAttPlaces[0].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            //Второй ряд
            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantAttPlaces[0].CurrentStigmaRow["require_skill1"].ToString(), CurrentCharacterRace);
            EnhantAttPlaces[1].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantAttPlaces[0].CurrentStigmaRow["require_skill2"].ToString(), CurrentCharacterRace);
            EnhantAttPlaces[2].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            //Третий ряд
            string rSkill1 = "require_skill1";
            string rSkill2 = "require_skill2";
            if (CurrentCharacterClass == Resources.Class_Elementalist ||
                CurrentCharacterClass == Resources.Class_Chanter)
            {
                rSkill1 = "require_skill2";
                rSkill2 = "require_skill1";
            }
            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantAttPlaces[1].CurrentStigmaRow[rSkill1].ToString(), CurrentCharacterRace);
            EnhantAttPlaces[3].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantAttPlaces[1].CurrentStigmaRow[rSkill2].ToString(), CurrentCharacterRace);
            EnhantAttPlaces[4].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            //Четвертый ряд
            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantAttPlaces[3].CurrentStigmaRow["require_skill1"].ToString(), CurrentCharacterRace);
            EnhantAttPlaces[5].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantAttPlaces[3].CurrentStigmaRow["require_skill2"].ToString(), CurrentCharacterRace);
            EnhantAttPlaces[6].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantAttPlaces[4].CurrentStigmaRow["require_skill1"].ToString(), CurrentCharacterRace);
            EnhantAttPlaces[7].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            OnPropertyChanged("EnhantAttPlaces");
        }

        private void FillEnchantedDefStigmas()
        {
            //Топовая стигма
            Dictionary<string, DataRow> stigmas =
                StigmaSet.CreateStigmaListForStigma(StigmaSet.GetGroupOfTopStigma(CurrentCharacterClass, true), CurrentCharacterRace);
            EnhantDefPlaces[0].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            //Второй ряд
            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantDefPlaces[0].CurrentStigmaRow["require_skill1"].ToString(), CurrentCharacterRace);
            EnhantDefPlaces[1].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantDefPlaces[0].CurrentStigmaRow["require_skill2"].ToString(), CurrentCharacterRace);
            EnhantDefPlaces[2].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            //Третий ряд
            string rSkill1 = "require_skill1";
            string rSkill2 = "require_skill2";
            if (CurrentCharacterClass == Resources.Class_Knight)
            {
                rSkill1 = "require_skill2";
                rSkill2 = "require_skill1";
            }

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantDefPlaces[1].CurrentStigmaRow[rSkill1].ToString(), CurrentCharacterRace);
            EnhantDefPlaces[3].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantDefPlaces[1].CurrentStigmaRow[rSkill2].ToString(), CurrentCharacterRace);
            EnhantDefPlaces[4].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            //Четвертый ряд
            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantDefPlaces[3].CurrentStigmaRow["require_skill1"].ToString(), CurrentCharacterRace);
            EnhantDefPlaces[5].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantDefPlaces[3].CurrentStigmaRow["require_skill2"].ToString(), CurrentCharacterRace);
            EnhantDefPlaces[6].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            stigmas = StigmaSet.CreateStigmaListForStigma(EnhantDefPlaces[4].CurrentStigmaRow["require_skill1"].ToString(), CurrentCharacterRace);
            EnhantDefPlaces[7].SetEnhantedPlace(stigmas, CurrentCharacterLevel, CurrentCharacterClass, CurrentCharacterRace);

            OnPropertyChanged("EnhantDefPlaces");
        }

        private void FillUsualStigmas()
        {
            List<string> uStigmas = Places.Where(place => place.Stigma.Stigmas != null && place.Stigma.Stigmas.Count > 0)
                                    .Select(place => place.Stigma.Stigmas[place.Stigma.CurrentStigmaKey]["skill_group_name"].ToString())
                                    .ToList();

            foreach (StigmaPlaceViewModel place in Places)
            {
                place.UsualStigmas = uStigmas;
            }
        }

        private void LoadCharacter()
        {
            string xfile = ClassFile.ShowOpenFileDialog("", ".xml", "XML file (.xml)|*.xml",
                                            DataModel.WorkDirectory + "\\Profiles\\");
            if (xfile == "") return;

            UiServices.SetBusyState();

            try
            {
                LoadProfileFromFile(xfile);
            }
            catch (Exception err)
            {
                ModernDialog md = new ModernDialog { Content = string.Format("{0}", err.Message) };
                md.ShowDialog();
                File.WriteAllText(@"error_file.txt", err.StackTrace);
            }
        }

        private void LoadProfileFromFile(string pathFile)
        {
            UiServices.SetBusyState();

            if (!File.Exists(pathFile)) throw new FileNotFoundException(string.Format("Файл не найден (File not found): {0}", pathFile));

            XDocument xLoad = XDocument.Load(pathFile);
            XElement xRoot = xLoad.Element("Stigmas");
            if (xRoot == null) throw new Exception("Неподдерживаемая версия файла (Wrong file-format version)!");

            XAttribute xVersion = xRoot.Attribute("Version");
            if (xVersion == null || xVersion.Value != "3.0") throw new Exception("Неподдерживаемая версия файла (Wrong file-format version)!");

            ClearPlaces();

            CurrentCharacterClass = ClassGameClass.GetGameClassNameFromProfileFile(xRoot.Attribute("GameClass").Value);
            CurrentCharacterLevel = Convert.ToByte(xRoot.Attribute("Level").Value);
            CurrentCharacterRace = Convert.ToBoolean(xRoot.Attribute("RaceA").Value);

            foreach (XElement xPlace in xRoot.Elements("Stigma").Where(xPlace => xPlace.Attribute("CurrentKey") != null))
            {
                StigmaPlaceViewModel stigmaPlace =
                    Places.Find(p => p.Index == Convert.ToByte(xPlace.Attribute("Index").Value));

                DataRow row = DataModel.GetRowAtName("Stigmas", xPlace.Attribute("CurrentKey").Value);
                stigmaPlace.Stigma = new StigmaCardViewModel(row, CurrentCharacterLevel, stigmaPlace.Index, _currentCharacterRace);

                stigmaPlace.Update();
            }

            StatusIsChange();

            foreach (StigmaPlaceViewModel place in Places.Where(place => place.Stigma.GetCurrentStigma() != null))
            {
                Mediator.Instance.NotifyColleagues(Messages.SetStigmaPlace,
                    new Tuple<string, string>("add", place.Stigma.GetCurrentStigma()["skill_group_name"].ToString()));
            }
        }

        private void SaveCharacter()
        {
            string xfile = ClassFile.ShowSaveFileDialog(ClassGameClass.GetGameClassNameForFileName(CurrentCharacterClass) + "_Stigmas.xml",
                                            ".xml", "XML (.xml)|*.xml", DataModel.WorkDirectory + "\\Profiles\\");
            if (xfile == "") return;

            UiServices.SetBusyState();

            XDocument xSave = new XDocument();
            XElement xRoot = new XElement("Stigmas");
            xSave.Add(xRoot);

            xRoot.Add(new XAttribute("Version", "3.0"));
            xRoot.Add(new XAttribute("GameClass", ClassGameClass.GetGameClassNameForProfileFile(CurrentCharacterClass)));
            xRoot.Add(new XAttribute("Level", CurrentCharacterLevel.ToString(CultureInfo.InvariantCulture)));
            xRoot.Add(new XAttribute("RaceA", CurrentCharacterRace.ToString()));

            foreach (StigmaPlaceViewModel place in Places)
            {
                xRoot.Add(place.Stigma.GetCardElementForSaveToFile());
            }

            xSave.Save(xfile);
        }

        private void StatusIsChange()
        {
            if (Places == null || string.IsNullOrEmpty(CurrentCharacterClass)) return;

            foreach (StigmaPlaceViewModel place in Places)
            {
                place.SetEnabled(CurrentCharacterClass, CurrentCharacterLevel, CurrentCharacterRace);
                place.SetRedBackground(CurrentCharacterLevel, CurrentCharacterClass);
            }

            FillEnchantedDefStigmas();
            FillEnchantedAttStigmas();
        }

        private void UpdateGeneralInfo(IList<int> info)
        {
            CalculateSums(info);
            FillUsualStigmas();
        }

        private void WireCommands()
        {
            SaveCharacterCommand = new RelayCommand(SaveCharacter) { IsEnabled = false };
            LoadCharacterCommand = new RelayCommand(LoadCharacter) { IsEnabled = true };
            ClearPlacesCommand = new RelayCommand(ClearPlaces) { IsEnabled = true };
        }

        private void GetNewStigmaFromEnhanted(Tuple<bool, StigmaCardViewModel> enhantedStigma)
        {
            if (StigmaSet.RequiredStigmaChains.ContainsKey(enhantedStigma.Item2.PlaceIndex))
            {
                Dictionary<StigmaCardViewModel, bool> requiredStigmas = GetRequiredStigmas(enhantedStigma);
                if (requiredStigmas == null) return;

                foreach (KeyValuePair<StigmaCardViewModel, bool> requiredStigma in requiredStigmas)
                {
                    foreach (StigmaPlaceViewModel place in Places)
                    {
                        if (requiredStigma.Value) if (!place.IsEnhanced) continue;
                        if (place.CurrentStigmaRow != null) continue;

                        byte placeIndex = place.Index;
                        place.Stigma = requiredStigma.Key;
                        place.Stigma.PlaceIndex = placeIndex;

                        place.CurrentStigmaRow = place.Stigma.GetCurrentStigma();
                        place.Update();

                        Mediator.Instance.NotifyColleagues(Messages.SetStigmaPlace,
                            new Tuple<string, string>("add", place.Stigma.GetCurrentStigma()["skill_group_name"].ToString()));

                        break;
                    }
                }
            }
            
            foreach (StigmaPlaceViewModel place in Places)
            {
                if (enhantedStigma.Item1) if (!place.IsEnhanced) continue;
                if (place.CurrentStigmaRow != null) continue;

                byte placeIndex = place.Index;
                place.Stigma = enhantedStigma.Item2;
                place.Stigma.PlaceIndex = placeIndex;

                place.CurrentStigmaRow = place.Stigma.GetCurrentStigma();
                place.Update();

                Mediator.Instance.NotifyColleagues(Messages.SetStigmaPlace,
                    new Tuple<string, string>("add", place.Stigma.GetCurrentStigma()["skill_group_name"].ToString()));

                break;
            }
        }

        private Dictionary<StigmaCardViewModel, bool> GetRequiredStigmas(Tuple<bool, StigmaCardViewModel> enhantedStigma)
        {
            List<StigmaPlaceViewModel> enhantedPlaces = EnhantDefPlaces.Any(s => s.Stigma == enhantedStigma.Item2)
                ? EnhantDefPlaces
                : EnhantAttPlaces;

            Dictionary<StigmaCardViewModel, bool> requiredStigmas = StigmaSet.RequiredStigmaChains[enhantedStigma.Item2.PlaceIndex]
                .ToDictionary(reqIndex => enhantedPlaces[reqIndex-1].Stigma, reqIndex => enhantedPlaces[reqIndex-1].IsEnhanced);

            foreach (StigmaPlaceViewModel place in Places.Where
                (place =>
                    place.CurrentStigmaRow != null && requiredStigmas.ContainsKey(place.Stigma)))
            {
                requiredStigmas.Remove(place.Stigma);
            }

            int freePlacesCount = Places.Count(place => place.CurrentStigmaRow == null);
            if (freePlacesCount < requiredStigmas.Values.Count())
            {
                ModernDialog dialog = new ModernDialog { Content = Resources.Stigma_ClearForInsert2 };
                dialog.Buttons = new[] { dialog.OkButton, dialog.CancelButton };
                dialog.ShowDialog();

                if (dialog.GetDialogResult == MessageBoxResult.OK)
                {
                    ClearPlaces();
                    return GetRequiredStigmas(enhantedStigma);
                }
                return null;
            }

            int freeEnhantedPlacesCount = Places.Where(place => place.CurrentStigmaRow == null).Count(place => place.IsEnhanced);
            if (freeEnhantedPlacesCount < requiredStigmas.Values.Count(s => s))
            {
                ModernDialog dialog = new ModernDialog { Content = Resources.Stigma_ClearForInsert };
                dialog.Buttons = new[] { dialog.OkButton, dialog.CancelButton };
                dialog.ShowDialog();
                if (dialog.GetDialogResult == MessageBoxResult.OK)
                {
                    ClearPlaces();
                    return GetRequiredStigmas(enhantedStigma);
                }
                return null;
            }

            return requiredStigmas;
        }
    }
}
